/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2023-2023.
 * Description: rtos ipi combine feature functions
 * Author: huyizhou
 * Create: 2023-09-14
 */

static void ipi_cpu_stop(unsigned int cpu);
static void ipi_complete(unsigned int cpu);

DEFINE_PER_CPU_ALIGNED(ipi_cpustat_t, combined_ipi_stat);
EXPORT_PER_CPU_SYMBOL(combined_ipi_stat);

static void show_combined_ipi_list(struct seq_file *p, int prec)
{
	unsigned int cpu, i;

	/* show the combined interrupts */
	for (i = MAX_IPI; i < NR_IPI; i++) {
		seq_printf(p, "%*s%u: ", prec - 1, "IPIC", i);

		for_each_online_cpu(cpu)
			seq_printf(p, "%10u ",
			__get_irq_stat(cpu, ipi_irqs[i - MAX_IPI]));

		seq_printf(p, " %s\n", ipi_types[i]);
	}
}

static void send_ipi_message(const struct cpumask *mask, enum ipi_msg_type msg)
{
	/*
	 * Call the platform specific cross-CPU call function.
	 */
	unsigned long flags;
	unsigned long msgs = 0;
	unsigned int cpu;

	if (msg == IPI_RESERVE) {
		printk("send wrong IPI 0 !!!\n");
		return;
	}
	if (msg >= IPI_CPU_STOP) {
		local_irq_save(flags);
		for_each_cpu(cpu, mask) {
			spin_lock(&(__get_irq_stat(cpu, lock)));
			msgs = __get_irq_stat(cpu, bits);
			msgs |= 1UL << (unsigned long)msg;
			__set_irq_stat(cpu, bits, msgs);
			spin_unlock(&(__get_irq_stat(cpu, lock)));
		}
		smp_cross_call(mask, IPI_COMBINE);
		local_irq_restore(flags);
	} else {
		smp_cross_call(mask, msg);
	}
}

static void ipi_combine_handler(unsigned int cpu, int ipinr)
{
	unsigned long msgs;

	for (;;) {
		spin_lock(&(__get_irq_stat(cpu, lock)));
		msgs = __get_irq_stat(cpu, bits);
		__set_irq_stat(cpu, bits, 0);
		spin_unlock(&(__get_irq_stat(cpu, lock)));
		if (!msgs) {
			/* Null IPI message!
			 * We clear eoi before handle_ipi. There may be a
			 * situation: we send second combine ipi before the
			 * first handle_ipi. Then we will get the null msg in
			 * the second handle_ipi.
			 */
			break;
		}

		do {
			unsigned long nextmsg;

			nextmsg = msgs & (~msgs + 1);
			msgs &= ~nextmsg;
			nextmsg = ffz(~nextmsg);
			if (nextmsg >= IPI_CPU_STOP &&
			    nextmsg <= IPI_COMPLETION) {
				trace_ipi_entry_rcuidle(ipi_types[nextmsg]);
				__inc_irq_stat(cpu,
					       ipi_irqs[nextmsg - MAX_IPI]);
			}

			switch (nextmsg) {
			case IPI_CPU_STOP:
				ipi_cpu_stop(cpu);
				break;

#ifdef CONFIG_IRQ_WORK
			case IPI_IRQ_WORK:
				irq_work_run();
				break;
#endif

			case IPI_COMPLETION:
				ipi_complete(cpu);
				break;

			case IPI_CPU_BACKTRACE:
				printk_nmi_enter();
				nmi_cpu_backtrace(get_irq_regs());
				printk_nmi_exit();
				break;

			default:
				pr_crit("CPU%u: Unknown IPI %d, message %ld\n",
							 cpu, ipinr, nextmsg);
				break;
			}
		} while (msgs);
	}
}
